package com.ry.video;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class FFmpegVideoEditor {
    private final String ffmpegPath;

    public FFmpegVideoEditor(String ffmpegPath) {
        this.ffmpegPath = ffmpegPath;
    }

    /**
     * 图片转视频
     *
     * @param imagePath  图片路径
     * @param outputPath 输出视频路径
     * @param duration   视频时长(秒)
     * @param fps        帧率
     * @throws IOException          异常
     * @throws InterruptedException 异常
     */
    public void imagesToVideo(String imagePath, String outputPath, int duration, int fps) throws IOException, InterruptedException {
        List<String> command = new ArrayList<>();
        command.add(ffmpegPath);
        command.add("-loop");
        command.add("1");
        command.add("-i");
        command.add(imagePath);
        command.add("-t");
        command.add(String.valueOf(duration));
        command.add("-r");
        command.add(String.valueOf(fps));
        command.add("-pix_fmt");
        command.add("yuv420p");
        command.add(outputPath);
        executeCommand(command);
    }

    /**
     * 视频合并
     *
     * @param videoPaths 视频路径列表
     * @param outputPath 输出视频路径
     * @throws IOException          异常
     * @throws InterruptedException 异常
     */
    public void mergeVideos(List<String> videoPaths, String outputPath) throws IOException, InterruptedException {
//        List<String> command = new ArrayList<>();
//        command.add(ffmpegPath);
//
//        for (String videoPath : videoPaths) {
//            command.add("-i");
//            command.add(videoPath);
//        }
//
//        command.add("-filter_complex");
//        StringBuilder filter = new StringBuilder();
//        for (int i = 0; i < videoPaths.size(); i++) {
//            filter.append("[").append(i).append(":v][").append(i).append(":a]");
//        }
//        filter.append("concat=n=").append(videoPaths.size()).append(":v=1:a=1[outv][outa]");
//        command.add(filter.toString());
//
//        command.add("-map");
//        command.add("[outv]");
//        command.add("-map");
//        command.add("[outa]");
//        command.add("-c:v");
//        command.add("libx264");
//        command.add("-c:a");
//        command.add("aac");
//        command.add(outputPath);


        List<String> command = new ArrayList<>();
        command.add("ffmpeg");
        command.add("-y");

        for (String input : videoPaths) {
            command.add("-i");
            command.add(input);
        }

        StringBuilder filterComplex = new StringBuilder();
        for (int i = 0; i < videoPaths.size(); i++) {
            filterComplex.append("[").append(i).append(":v]");
        }
        filterComplex.append("concat=n=").append(videoPaths.size()).append(":v=1[outv]");

        command.add("-filter_complex");
        command.add(filterComplex.toString());
        command.add("-map");
        command.add("[outv]");
        command.add("-c:v");
        command.add("libx264");
        command.add(outputPath);
        executeCommand(command);
    }

    /**
     * 字幕合并
     *
     * @param videoPath    视频路径
     * @param subtitlePath 字幕路径
     * @param outputPath   输出视频路径
     * @throws IOException          异常
     * @throws InterruptedException 异常
     */
    public void mergeSubtitles(String videoPath, String subtitlePath, String outputPath) throws IOException, InterruptedException {
        List<String> command = new ArrayList<>();
        command.add(ffmpegPath);
        command.add("-i");
        command.add(videoPath);
        command.add("-i");
        command.add(subtitlePath);
        command.add("-c:v");
        command.add("copy");
        command.add("-c:a");
        command.add("copy");
        command.add("-c:s");
        command.add("mov_text");
        command.add(outputPath);
        executeCommand(command);
    }

    /**
     * 添加口播
     *
     * @param videoPath  视频路径
     * @param voicePath  口播音频路径
     * @param outputPath 输出视频路径
     * @throws IOException          异常
     * @throws InterruptedException 异常
     */
    public void addVoice(String videoPath, String voicePath, String outputPath) throws IOException, InterruptedException {
        List<String> command = new ArrayList<>();
        command.add(ffmpegPath);
        command.add("-i");
        command.add(videoPath);
        command.add("-i");
        command.add(voicePath);
        command.add("-c:v");
        command.add("copy");
        command.add("-map");
        command.add("0:v:0");
        command.add("-map");
        command.add("1:a:0");
        command.add("-shortest");
        command.add(outputPath);
        executeCommand(command);
    }

    /**
     * 添加背景音乐
     *
     * @param videoPath  视频路径
     * @param bgmPath    背景音乐路径
     * @param outputPath 输出视频路径
     * @param volume     背景音乐音量(0.0-1.0)
     * @throws IOException          异常
     * @throws InterruptedException 异常
     */
    public void addBackgroundMusic(String videoPath, String bgmPath, String outputPath, double volume) throws IOException, InterruptedException {
        List<String> command = new ArrayList<>();
        command.add(ffmpegPath);
        command.add("-i");
        command.add(videoPath);
        command.add("-i");
        command.add(bgmPath);
        command.add("-filter_complex");
        command.add("[1:a]volume=" + volume + "[bgm];[0:a][bgm]amix=inputs=2:duration=first:dropout_transition=0[outa]");
        command.add("-map");
        command.add("0:v");
        command.add("-map");
        command.add("[outa]");
        command.add("-c:v");
        command.add("copy");
        command.add("-shortest");
        command.add(outputPath);
        executeCommand(command);
    }

    /**
     * 切割视频片段
     * <p>
     * 该方法使用FFmpeg工具从输入视频中提取指定时间段的片段，并将其保存为新的视频文件
     * 它通过构建FFmpeg命令行指令并使用ProcessBuilder执行这些指令来实现视频切割功能
     *
     * @param inputVideo 输入视频文件的路径
     * @param start      开始切割的时间点（以秒为单位）
     * @param end        结束切割的时间点（以秒为单位）
     * @param outputPath 输出视频文件的路径
     * @return 返回一个布尔值，指示视频切割是否成功如果返回true，则表示视频切割成功
     * @throws IOException          当无法启动进程或发生I/O错误时抛出此异常
     * @throws InterruptedException 如果在等待进程完成时线程被中断抛出此异常
     */
    public void cutVideo(String inputVideo, double start, double end, String outputPath) throws IOException, InterruptedException {
        // 构建FFmpeg命令行指令
        List<String> command = new ArrayList<>();
        command.add("ffmpeg");
        command.add("-y"); // 覆盖输出文件，如果不希望覆盖，请删除此行
        command.add("-i");
        command.add(inputVideo);
        command.add("-ss");
        command.add(String.valueOf(start));
        command.add("-to");
        command.add(String.valueOf(end));
        command.add("-c:v");
        command.add("libx264"); // 使用libx264编码器对视频进行编码
        command.add("-c:a");
        command.add("aac"); // 使用aac编码器对音频进行编码
        command.add(outputPath);
        executeCommand(command);
    }


    /**
     * 执行FFmpeg命令
     *
     * @param command 命令列表
     * @throws IOException          异常
     * @throws InterruptedException 异常
     */
    private void executeCommand(List<String> command) throws IOException, InterruptedException {
        ProcessBuilder processBuilder = new ProcessBuilder(command);
        Process process = processBuilder.start();

        // 读取标准输出
        Thread stdoutThread = new Thread(() -> {
            try (InputStream is = process.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr)) {
                String line;
                while ((line = br.readLine()) != null) {
                    System.out.println(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        stdoutThread.start();

        // 读取错误输出
        Thread stderrThread = new Thread(() -> {
            try (InputStream is = process.getErrorStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr)) {
                String line;
                while ((line = br.readLine()) != null) {
                    System.err.println(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        stderrThread.start();

        // 等待进程结束
        int exitCode = process.waitFor();
        stdoutThread.join();
        stderrThread.join();

        if (exitCode != 0) {
            throw new RuntimeException("FFmpeg command failed with exit code " + exitCode);
        }
    }

//    public static void main(String[] args) {
//        long startTime = System.currentTimeMillis();
//        long endTime = System.currentTimeMillis();
//        try {
//            // 使用示例
//            FFmpegVideoEditor editor = new FFmpegVideoEditor("ffmpeg");
//
//            String imagePath = "material/image1.jpg";
//            String imageToVideo = "material/out/video/output1.mp4";
//            String mergeVideo = "material/out/video/mergre1.mp4";
//            String videoPath = "material/video1.mp4";
//            // 图片转视频
////            editor.imagesToVideo(imagePath, imageToVideo, 10, 30);
//
//            // 视频合并
//            List<String> videoPaths = new ArrayList<>();
//            for (int i = 0; i < 10; i++) {
//                videoPaths.add(videoPath);
//            }
//            editor.mergeVideos(videoPaths, mergeVideo);
//            endTime = System.currentTimeMillis();
//            System.out.printf("10个视频合成30S,共花费时间：" + (endTime - startTime) / 1000.0 + "秒");
//
//            // 字幕合并
////            editor.mergeSubtitles("/path/to/video.mp4", "/path/to/subtitle.srt", "/path/to/output3.mp4");
//
//            // 添加口播
////            editor.addVoice("/path/to/video.mp4", "/path/to/voice.mp3", "/path/to/output4.mp4");
//
//            // 添加背景音乐
////            editor.addBackgroundMusic("/path/to/video.mp4", "/path/to/bgm.mp3", "/path/to/output5.mp4", 0.5);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }


    public static void main(String[] args) {
        int threadCount = 1;

        // 创建固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(threadCount);

        String videoPath = "material/video1.mp4";
        String outputPathBase = "material/out/video/merged_output";

        for (int i = 0; i < threadCount; i++) {
            int taskId = i + 1;
            executorService.submit(() -> {
                try {
                    FFmpegVideoEditor editor = new FFmpegVideoEditor("ffmpeg");

                    List<String> videoPaths = new ArrayList<>();
                    for (int j = 0; j < 10; j++) {
                        videoPaths.add(videoPath);
                    }

                    String outputFilePath = outputPathBase + "_" + taskId + ".mp4";

                    long startTime = System.currentTimeMillis();
                    editor.mergeVideos(videoPaths, outputFilePath);
                    long endTime = System.currentTimeMillis();

                    System.out.printf("任务 %d 完成，输出: %s，耗时 %.2f 秒%n",
                            taskId, outputFilePath, (endTime - startTime) / 1000.0);
                } catch (Exception e) {
                    System.err.println("任务执行失败: " + e.getMessage());
                    e.printStackTrace();
                }
            });
        }

        // 关闭线程池
        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(30, TimeUnit.MINUTES)) {
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            executorService.shutdownNow();
        }
    }
}